package com.smssalama.transport;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.Date;
import java.util.Vector;

import javax.microedition.io.Connector;
import javax.microedition.lcdui.Display;
import javax.wireless.messaging.BinaryMessage;
import javax.wireless.messaging.Message;
import javax.wireless.messaging.MessageConnection;
import javax.wireless.messaging.MessageListener;
import javax.wireless.messaging.TextMessage;

import com.smssalama.SMSSalama;
import com.smssalama.Utils;

/**
 * Wraps a {@link MessageConnection} instance and handle the low level API for sending and receiving messages.
 * 
 * @author Arnold P. Minde
 *
 */
public class SMSTransport {
	public static final int STATUS_SENT = 1;
	public static final int SATUS_FAILED = -1;

	private MessageConnection smsconn;
	private int port;
	private boolean active;
	private ISMSTransportListener listener;
	private Vector listeners;
	
	public SMSTransport(ISMSTransportListener listener) {
		this(Utils.getSMSSalamaPort(), listener);
	}

	public SMSTransport(int port, ISMSTransportListener listener) {
		this.port = port;
		this.listener = listener;
	}

	public SMSTransport(int port, ISMSTransportListener listener,
			MessageConnection smsconn) throws IOException {
		this(port, listener);
		this.smsconn = smsconn;
		this.active = true;
		handleConnection();
	}

	public int getPort() {
		return this.port;
	}

	public void open() throws IOException {
		this.smsconn = (MessageConnection) Connector.open("sms://:" + this.port);
		handleConnection();		
		this.active = true;
	}
	
	public boolean isActive() {
		return active;
	}

	public void addTransportListener(ISMSTransportListener listener){
		if(listeners==null){
			listeners = new Vector();
		}
		listeners.addElement(listener);
	}

	public void removeTransportListener(ISMSTransportListener listener){
		if(listeners==null) return;
		listeners.removeElement(listener);
	}

	private void handleConnection() throws IOException {
		//We could use message listener to get notified on new messages
		this.smsconn.setMessageListener(new MessageListener() {
			public void notifyIncomingMessage(MessageConnection message) {
				try {
					Message msg = message.receive();
					handleMessage(msg);
				} catch (InterruptedIOException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
		//Or we could as well use polling on our own thread
		/*
		Runnable receiver = new Runnable() {
			public void run() {
				while (true) {
					try {
						Message message = SMSTransport.this.smsconn.receive();
						handleMessage(message);
					} catch (java.io.InterruptedIOException e) {
						// Messageconnextion was closed, exit
						return;
					} catch (IOException e) {
						// Some other exception, ignore
						e.printStackTrace();
					}
				}
			}
		};
		new Thread(receiver).start();
		*/
	}

	public void close() throws IOException {
		this.smsconn.close();
		this.smsconn = null;
		this.active = false;
	}
	
	protected void checkActive(){
		if(!this.active){
			throw new RuntimeException("The transport is not active");
		}
	}
	
	protected void doSend(final int messageId, final Message message, final SendMessageCallback callback) {
		//Send the message in a different thread, synchronously
		final Runnable runnable = new Runnable() {
			public void run() {
				try {
					SMSTransport.this.smsconn.send(message);
					if(callback!=null){
						//if SMS Salama Midlet is running, serialize notification to callback
						//Otherwise, call it in this thread
						if(SMSSalama.getInstance()!=null){
							Display.getDisplay(SMSSalama.getInstance()).callSerially(new Runnable(){
								public void run() {
									callback.onSuccess();
								}});
						}else{
							callback.onSuccess();
						}						
					}
					/*
					if(SMSTransport.this.listener!=null){
						SMSTransport.this.listener.onMessageSendingStatus(null,
								messageId, SMSTransport.STATUS_SENT);
					}
					*/
				} catch (final Exception e) {
					e.printStackTrace();
					/*
					SMSTransport.this.listener.onMessageSendingStatus(null,
							messageId, SMSTransport.SATUS_FAILED);
					*/
					if(callback!=null){
						if(SMSSalama.getInstance()!=null){
							Display.getDisplay(SMSSalama.getInstance()).callSerially(new Runnable(){
								public void run() {
									callback.onFailure(e);
								}});
						}else{
							callback.onFailure(e);
						}
					}
				}
			}
		};
		new Thread(runnable).start();
	}

	public void sendBinarySMS(int messageId, String number, int port,
			byte[] data, final SendMessageCallback callback) {
		checkActive();
		// Thread.sleep(3000);
		String address = "sms://" + number;
		if (port != 0) {
			address = address + ":" + port;
		}
		BinaryMessage message = (BinaryMessage) this.smsconn.newMessage(
				MessageConnection.BINARY_MESSAGE, address);
		message.setAddress(address);
		message.setPayloadData(data);
		doSend(messageId, message, callback);
	}

	public void sendTextSMS(int messageId, String number, int port, String text, final SendMessageCallback callback) {
		checkActive();
		String address = "sms://" + number;
		if (port > 0) {
			address = address + ":" + port;
		}
		// Thread.sleep(3000);
		// System.out.println(text);
		TextMessage message = (TextMessage) this.smsconn.newMessage(
				MessageConnection.TEXT_MESSAGE, address);
		message.setPayloadText(text);
		doSend(messageId, message, callback);
	}

	protected String parserNumberFromAddress(String address) {
		String number = address.substring(6);
		if (number != null && number.indexOf(':') > 0) {
			number = number.substring(0, number.indexOf(':'));
		}
		return number;

	}

	protected void handleMessage(Message message) {
		if (message instanceof TextMessage) {
			String payload = ((TextMessage) message).getPayloadText();
			String number = parserNumberFromAddress(((TextMessage) message)
					.getAddress());
			Date timestamp = ((TextMessage) message).getTimestamp();
			this.listener.onTextMessage(null, number, timestamp, payload);
			;
		} else if (message instanceof BinaryMessage) {
			byte[] payload = ((BinaryMessage) message).getPayloadData();
			String number = parserNumberFromAddress(((BinaryMessage) message)
					.getAddress());
			Date timestamp = ((BinaryMessage) message).getTimestamp();
			this.listener.onBinaryMessage(null, number, timestamp, payload);
			;
		}
	}
}
